home *** CD-ROM | disk | FTP | other *** search
/ Linux Cubed Series 7: Sunsite / Linux Cubed Series 7 - Sunsite Vol 1.iso / system / admin / time / adjtimex.2 / adjtimex / clockdiff.c < prev    next >
C/C++ Source or Header  |  1995-03-08  |  7KB  |  289 lines

  1. /*
  2.  * compare - print difference between system time and CMOS time
  3.  *
  4.  * Writes to stdout: 
  5.  *    system time (seconds since 1/1/70 per RTC), 
  6.  *    system time - RTC time, 
  7.  *      drift: change in system time - RTC time since last report,
  8.  *    frequency offset (in kernel, ((time_freq+1) >> (SHIFT_KF - 16)))
  9.  *     time offset (in kernel, time_adjust) 
  10.  * Takes into account systematic error in RTC as recorded in /etc/adjtime, 
  11.  * just like clock -a.
  12.  *
  13.  * AUTHORS 
  14.  *      Jim Van Zandt <jrv@vanzandt.mv.com> 
  15.  * Uses code from clock(8), which had the following contributors: 
  16.  *      Charles Hedrick <hedrick@cs.rutgers.edu>
  17.  *      Rob Hooft <hooft@EMBL-Heidelberg.DE> 
  18.  *      Harald Koenig (koenig@nova.tat.physik.uni-tuebingen.de>
  19.  * Also uses code from adjtimex(8), by:
  20.  *      Steven S. Dick <ssd@nevets.oau.org>
  21.  */
  22. #include <stdio.h>
  23. #include <errno.h>
  24. #include <stdlib.h>
  25. #include <unistd.h>
  26. #include <time.h>
  27. #include <string.h>
  28. #include <limits.h>
  29. #include <sys/types.h>
  30. #include <sys/time.h>
  31. #include <syscall.h>
  32. #include <sys/timex.h>
  33.  
  34. _syscall1(int, adjtimex, struct timex *, txcp)
  35. #define USE_INLINE_ASM_IO
  36.  
  37.  /* Here the information for time adjustments is kept. */
  38. #define ADJPATH "/etc/adjtime"
  39.  
  40.  /* used for debugging the code. */
  41.  /* #define DEBUG */
  42.  
  43.  /* Stupid constants */
  44. #define SECONDSPERDAY 86400
  45.  
  46.  /* Globals */
  47. int universal = 0;
  48.  
  49.  
  50. #ifndef USE_INLINE_ASM_IO
  51. int cmos_fd;
  52.  
  53. #endif
  54.  
  55. #define CMOS_READ(addr)      ({outb(0x70,(addr)|0x80); inb(0x71);})
  56. #define CMOS_WRITE(addr,val) ({outb(0x70,(addr)|0x80); outb(0x71,(val)); })
  57.  
  58. static inline void
  59. outb(short port, char val)
  60. {
  61. #ifdef USE_INLINE_ASM_IO
  62.     __asm__ volatile ("out%B0 %0,%1"::"a" (val), "d"(port));
  63.  
  64. #else
  65.      lseek(cmos_fd, port, 0);
  66.     write(cmos_fd, &val, 1);
  67. #endif
  68. }
  69.  
  70. static inline unsigned char
  71. inb(short port)
  72. {
  73.     unsigned char ret;
  74.  
  75. #ifdef USE_INLINE_ASM_IO
  76.     __asm__ volatile ("in%B0 %1,%0":"=a" (ret):"d"(port));
  77.  
  78. #else
  79.     lseek(cmos_fd, port, 0);
  80.     read(cmos_fd, &ret, 1);
  81. #endif
  82.     return ret;
  83. }
  84.  
  85. void
  86. cmos_init()
  87. {
  88. #ifdef USE_INLINE_ASM_IO
  89.     if (ioperm(0x70, 2, 1)) {
  90.         fprintf(stderr, "clock: unable to get I/O port access\n");
  91.         exit(1);
  92.     }
  93. #else
  94.     cmos_fd = open("/dev/port", 2);
  95.     if (cmos_fd < 0) {
  96.         perror("unable to open /dev/port read/write : ");
  97.         exit(1);
  98.     }
  99.     if (lseek(cmos_fd, 0x70, 0) < 0 || lseek(cmos_fd, 0x71, 0) < 0) {
  100.         perror("unable to seek port 0x70 in /dev/port : ");
  101.         exit(1);
  102.     }
  103. #endif
  104. }
  105.  
  106. static inline int
  107. cmos_read_bcd(int addr)
  108. {
  109.     int b;
  110.  
  111.     b = CMOS_READ(addr);
  112.     return (b & 15) + (b >> 4) * 10;
  113. }
  114.  
  115. volatile void
  116. usage()
  117. {
  118.     fprintf(stderr,
  119.         "compare  [-p N]\n"
  120.         "  -p N  repeat every N seconds, forever\n"
  121.     );
  122.     exit(1);
  123. }
  124.  
  125. int
  126. main(int argc, char **argv, char **envp)
  127. {
  128.     struct timex txc;
  129.     struct tm tm;
  130.     time_t cmos_time;
  131.     time_t last_time;
  132.     double cmos_sec, system_sec, dif, dif_prev=0.;
  133.     char *zone;
  134.     char zonebuf[256];
  135.     FILE *adj;
  136.     double factor;
  137.     double cmos_adjustment;
  138.     double not_adjusted;
  139.     int i;
  140.     int interval = 0;
  141.     int arg;
  142.     extern char *optarg;
  143.     struct timeval now;
  144.  
  145.     while ((arg = getopt(argc, argv, "p:")) != -1) {
  146.         switch (arg) {
  147.         case 'p':
  148.             /* reading RTC takes 1 sec */
  149.             interval = atoi(optarg) - 1;
  150.             if (interval <= 0) {
  151.                 fprintf(stderr, "repeat interval out of range\n");
  152.                 exit(1);
  153.             }
  154.             break;
  155.         case ':':
  156.             fprintf(stderr, "missing parameter\n");
  157.             break;
  158.         default:
  159.             usage();
  160.         }
  161.     }
  162.  
  163.     while (1) {
  164.         cmos_init();
  165.  
  166.         /* Read adjustment parameters first */
  167.         if ((adj = fopen(ADJPATH, "r")) == NULL) {
  168.             perror(ADJPATH);
  169.             exit(2);
  170.         }
  171.         if (fscanf(adj, "%lf %ld %lf", &factor, &last_time, 
  172.                         ¬_adjusted) < 0) {
  173.             perror(ADJPATH);
  174.             exit(2);
  175.         }
  176.         fclose(adj);
  177. #ifdef DEBUG
  178.         printf("Last adjustment done at %ld seconds after 1/1/1970\n", (long) last_time);
  179. #endif
  180.  
  181.  
  182.         /* read RTC exactly on falling edge of update flag */
  183.         /* Wait for rise.... (may take up to 1 second) */
  184.  
  185.         for (i = 0; i < 10000000; i++)
  186.             if (CMOS_READ(10) & 0x80)
  187.                 break;
  188.  
  189.         /* Wait for fall.... (must try at least 2.228 ms) */
  190.  
  191.         for (i = 0; i < 1000000; i++)
  192.             if (!(CMOS_READ(10) & 0x80))
  193.                 break;
  194.  
  195.         /* The "do" loop is "low-risk programming" */
  196.         /* In theory it should never run more than once */
  197.         do {
  198.             tm.tm_sec = cmos_read_bcd(0);
  199.             tm.tm_min = cmos_read_bcd(2);
  200.             tm.tm_hour = cmos_read_bcd(4);
  201.             tm.tm_wday = cmos_read_bcd(6);
  202.             tm.tm_mday = cmos_read_bcd(7);
  203.             tm.tm_mon = cmos_read_bcd(8);
  204.             tm.tm_year = cmos_read_bcd(9);
  205.         }
  206.         while (tm.tm_sec != cmos_read_bcd(0));
  207.  
  208.         /* fetch system time immediately */
  209.         gettimeofday(&now, NULL);
  210.  
  211.  
  212.         tm.tm_mon--;    /* DOS uses 1 base */
  213.         tm.tm_wday -= 3;/* DOS uses 3 - 9 for week days */
  214.         tm.tm_isdst = -1;    /* don't know whether it's daylight */
  215. #ifdef DEBUG
  216.         printf(" mday=%d  mon=%d  wday=%d  year=%d\n",
  217.                tm.tm_mday, tm.tm_mon, tm.tm_wday, tm.tm_year);
  218.         printf("Cmos time  %d:%02d:%02d\n", tm.tm_hour, tm.tm_min, tm.tm_sec);
  219. #endif
  220.  
  221.     /*
  222.      * Mktime assumes we're giving it local time.  If the CMOS clock is in
  223.      * GMT, we have to set up TZ to mktime knows it.  Tzset gets called
  224.      * implicitly by the time code, but only the first time.  When
  225.      * changing the environment variable, better call tzset explicitly.
  226.      */
  227.         if (universal) {
  228.             zone = (char *) getenv("TZ");    /* save original time zone */
  229.             (void) putenv("TZ=");
  230.             tzset();
  231.             cmos_time = mktime(&tm);
  232.             /* now put back the original zone */
  233.             if (zone) {
  234.                 if ((strlen(zone) + 4) > sizeof(zonebuf)) {
  235.                     fprintf(stderr, "Size of TZ variable is too long\n");
  236.                     exit(2);
  237.                 }
  238.                 strcpy(zonebuf, "TZ=");
  239.                 strcat(zonebuf, zone);
  240.                 putenv(zonebuf);
  241.             } else {/* wasn't one, so clear it */
  242.                 putenv("TZ");
  243.             }
  244.             tzset();
  245.             printf("%s", ctime(&cmos_time));
  246.         } else {
  247.             (void) mktime(&tm);    /* fix wday */
  248.             /* printf ("%s", asctime (&tm)); */
  249.         }
  250.  
  251.  
  252.         if (universal)
  253.             (void) putenv("TZ=");
  254.         cmos_time = mktime(&tm);
  255.         system_sec = now.tv_sec + .000001 * now.tv_usec;
  256. #ifdef DEBUG
  257.         printf("Number of seconds since 1/1/1970 is %ld\n", 
  258.                             (long) cmos_time);
  259. #endif
  260.         cmos_adjustment = ((double) (cmos_time - last_time))
  261.             * factor / SECONDSPERDAY
  262.             + not_adjusted;
  263.         cmos_sec = cmos_time + cmos_adjustment;
  264. #ifdef DEBUG
  265.         printf("Time since last adjustment is %d sec",
  266.                (int) (cmos_time - last_time));
  267.         printf(", now needs adjustment by %9.6f sec\n", cmos_adjustment);
  268. #endif
  269.         dif = system_sec - cmos_sec;
  270.         
  271.         txc.mode = 0;
  272.         if (adjtimex(&txc) < 0)
  273.             perror("compare: adjtimex");
  274.             
  275.         printf("%9ld   %9.6f %9.6f %9ld %9ld %9ld\n",
  276.                (long) cmos_sec,
  277.                dif,
  278.                dif - dif_prev,
  279.                txc.frequency,
  280.                txc.tick,
  281.                txc.offset);
  282.         dif_prev = dif;
  283.         if(interval == 0)
  284.             break;
  285.         sleep(interval);
  286.     }
  287.     return 0;
  288. }
  289.